home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / gst-python / 0.10 / examples / remuxer.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-04-20  |  32.2 KB  |  823 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. import pygtk
  5. pygtk.require('2.0')
  6. import sys
  7. import gobject
  8. gobject.threads_init()
  9. import pygst
  10. pygst.require('0.10')
  11. import gst
  12. import gst.interfaces as gst
  13. import gtk
  14.  
  15. class GstPlayer:
  16.     
  17.     def __init__(self, videowidget):
  18.         self.playing = False
  19.         self.player = gst.element_factory_make('playbin', 'player')
  20.         self.videowidget = videowidget
  21.         bus = self.player.get_bus()
  22.         bus.enable_sync_message_emission()
  23.         bus.add_signal_watch()
  24.         bus.connect('sync-message::element', self.on_sync_message)
  25.         bus.connect('message', self.on_message)
  26.  
  27.     
  28.     def on_sync_message(self, bus, message):
  29.         if message.structure is None:
  30.             return None
  31.         if message.structure.get_name() == 'prepare-xwindow-id':
  32.             gtk.gdk.display_get_default().sync()
  33.             self.videowidget.set_sink(message.src)
  34.             message.src.set_property('force-aspect-ratio', True)
  35.         
  36.  
  37.     
  38.     def on_message(self, bus, message):
  39.         t = message.type
  40.         if t == gst.MESSAGE_ERROR:
  41.             (err, debug) = message.parse_error()
  42.             print 'Error: %s' % err, debug
  43.             if self.on_eos:
  44.                 self.on_eos()
  45.             
  46.             self.playing = False
  47.         elif t == gst.MESSAGE_EOS:
  48.             if self.on_eos:
  49.                 self.on_eos()
  50.             
  51.             self.playing = False
  52.         
  53.  
  54.     
  55.     def set_location(self, location):
  56.         self.player.set_state(gst.STATE_NULL)
  57.         self.player.set_property('uri', location)
  58.  
  59.     
  60.     def get_location(self):
  61.         return self.player.get_property('uri')
  62.  
  63.     
  64.     def query_position(self):
  65.         '''Returns a (position, duration) tuple'''
  66.         
  67.         try:
  68.             (position, format) = self.player.query_position(gst.FORMAT_TIME)
  69.         except:
  70.             position = gst.CLOCK_TIME_NONE
  71.  
  72.         
  73.         try:
  74.             (duration, format) = self.player.query_duration(gst.FORMAT_TIME)
  75.         except:
  76.             duration = gst.CLOCK_TIME_NONE
  77.  
  78.         return (position, duration)
  79.  
  80.     
  81.     def seek(self, location):
  82.         '''
  83.         @param location: time to seek to, in nanoseconds
  84.         '''
  85.         gst.debug('seeking to %r' % location)
  86.         event = gst.event_new_seek(1, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, gst.SEEK_TYPE_SET, location, gst.SEEK_TYPE_NONE, 0)
  87.         res = self.player.send_event(event)
  88.         if res:
  89.             gst.info('setting new stream time to 0')
  90.             self.player.set_new_stream_time(0x0L)
  91.         else:
  92.             gst.error('seek to %r failed' % location)
  93.  
  94.     
  95.     def pause(self):
  96.         gst.info('pausing player')
  97.         self.player.set_state(gst.STATE_PAUSED)
  98.         self.playing = False
  99.  
  100.     
  101.     def play(self):
  102.         gst.info('playing player')
  103.         self.player.set_state(gst.STATE_PLAYING)
  104.         self.playing = True
  105.  
  106.     
  107.     def stop(self):
  108.         self.player.set_state(gst.STATE_NULL)
  109.         gst.info('stopped player')
  110.  
  111.     
  112.     def get_state(self, timeout = 1):
  113.         return self.player.get_state(timeout = timeout)
  114.  
  115.     
  116.     def is_playing(self):
  117.         return self.playing
  118.  
  119.  
  120.  
  121. class VideoWidget(gtk.DrawingArea):
  122.     
  123.     def __init__(self):
  124.         gtk.DrawingArea.__init__(self)
  125.         self.imagesink = None
  126.         self.unset_flags(gtk.DOUBLE_BUFFERED)
  127.  
  128.     
  129.     def do_expose_event(self, event):
  130.         if self.imagesink:
  131.             self.imagesink.expose()
  132.             return False
  133.         return True
  134.  
  135.     
  136.     def set_sink(self, sink):
  137.         if not self.window.xid:
  138.             raise AssertionError
  139.         self.imagesink = sink
  140.         self.imagesink.set_xwindow_id(self.window.xid)
  141.  
  142.  
  143.  
  144. class TimeControl(gtk.HBox):
  145.     sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
  146.     __gproperties__ = {
  147.         'time': (gobject.TYPE_UINT64, 'Time', 'Time', 0x0L, 0x7FFFFFFFFFFFFFFFL, 0x0L, gobject.PARAM_READABLE) }
  148.     
  149.     def __init__(self, window, label):
  150.         gtk.HBox.__init__(self)
  151.         self.pwindow = window
  152.         self.label = label
  153.         self.create_ui()
  154.  
  155.     
  156.     def get_property(self, param, pspec):
  157.         if param == 'time':
  158.             return self.get_time()
  159.         if not param in self.__gproperties__:
  160.             raise AssertionError, 'Unknown property: %s' % param
  161.  
  162.     
  163.     def create_ui(self):
  164.         label = gtk.Label(self.label + ': ')
  165.         label.show()
  166.         a = gtk.Alignment(1, 0.5)
  167.         a.add(label)
  168.         a.set_padding(0, 0, 12, 0)
  169.         a.show()
  170.         self.sizegroup.add_widget(a)
  171.         self.pack_start(a, True, False, 0)
  172.         self.minutes = minutes = gtk.Entry(5)
  173.         minutes.set_width_chars(5)
  174.         minutes.set_alignment(1)
  175.         minutes.connect(('changed',), (lambda : self.notify('time')))
  176.         minutes.connect_after(('activate',), (lambda : self.activated()))
  177.         label2 = gtk.Label(':')
  178.         self.seconds = seconds = gtk.Entry(2)
  179.         seconds.set_width_chars(2)
  180.         seconds.set_alignment(1)
  181.         seconds.connect(('changed',), (lambda : self.notify('time')))
  182.         seconds.connect_after(('activate',), (lambda : self.activated()))
  183.         label3 = gtk.Label('.')
  184.         self.milliseconds = milliseconds = gtk.Entry(3)
  185.         milliseconds.set_width_chars(3)
  186.         milliseconds.set_alignment(0)
  187.         milliseconds.connect(('changed',), (lambda : self.notify('time')))
  188.         milliseconds.connect_after(('activate',), (lambda : self.activated()))
  189.         set = gtk.Button('Set')
  190.         goto = gtk.Button('Go')
  191.         goto.set_property('image', gtk.image_new_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_BUTTON))
  192.         for w in (minutes, label2, seconds, label3, milliseconds):
  193.             w.show()
  194.             self.pack_start(w, False)
  195.         
  196.         set.show()
  197.         self.pack_start(set, False, False, 6)
  198.         goto.show()
  199.         self.pack_start(goto, False, False, 0)
  200.         set.connect(('clicked',), (lambda : self.set_now()))
  201.         goto.connect(('clicked',), (lambda : self.activated()))
  202.         pad = gtk.Label('')
  203.         pad.show()
  204.         self.pack_start(pad, True, False, 0)
  205.  
  206.     
  207.     def get_time(self):
  208.         time = 0
  209.         for w, multiplier in ((self.minutes, gst.SECOND * 60), (self.seconds, gst.SECOND), (self.milliseconds, gst.MSECOND)):
  210.             text = w.get_text()
  211.             
  212.             try:
  213.                 val = int(text)
  214.             except ValueError:
  215.                 val = 0
  216.  
  217.             if not val or str(val):
  218.                 pass
  219.             w.set_text('0')
  220.             time += val * multiplier
  221.         
  222.         return time
  223.  
  224.     
  225.     def set_time(self, time):
  226.         if time == gst.CLOCK_TIME_NONE:
  227.             print "Can't set '%s' (invalid time)" % self.label
  228.             return None
  229.         self.freeze_notify()
  230.         for w, multiplier in ((self.minutes, gst.SECOND * 60), (self.seconds, gst.SECOND), (self.milliseconds, gst.MSECOND)):
  231.             val = time // multiplier
  232.             w.set_text(str(val))
  233.             time -= val * multiplier
  234.         
  235.         self.thaw_notify()
  236.  
  237.     
  238.     def set_now(self):
  239.         (time, dur) = self.pwindow.player.query_position()
  240.         self.set_time(time)
  241.  
  242.     
  243.     def activated(self):
  244.         time = self.get_time()
  245.         if self.pwindow.player.is_playing():
  246.             self.pwindow.play_toggled()
  247.         
  248.         self.pwindow.player.seek(time)
  249.         self.pwindow.player.get_state(timeout = gst.MSECOND * 200)
  250.  
  251.  
  252.  
  253. class ProgressDialog(gtk.Dialog):
  254.     
  255.     def __init__(self, title, description, task, parent, flags, buttons):
  256.         gtk.Dialog.__init__(self, title, parent, flags, buttons)
  257.         self._create_ui(title, description, task)
  258.  
  259.     
  260.     def _create_ui(self, title, description, task):
  261.         self.set_border_width(6)
  262.         self.set_resizable(False)
  263.         self.set_has_separator(False)
  264.         vbox = gtk.VBox()
  265.         vbox.set_border_width(6)
  266.         vbox.show()
  267.         self.vbox.pack_start(vbox, False)
  268.         label = gtk.Label('<big><b>%s</b></big>' % title)
  269.         label.set_use_markup(True)
  270.         label.set_alignment(0, 0)
  271.         label.show()
  272.         vbox.pack_start(label, False)
  273.         label = gtk.Label(description)
  274.         label.set_use_markup(True)
  275.         label.set_alignment(0, 0)
  276.         label.set_line_wrap(True)
  277.         label.set_padding(0, 12)
  278.         label.show()
  279.         vbox.pack_start(label, False)
  280.         self.progress = progress = gtk.ProgressBar()
  281.         progress.show()
  282.         vbox.pack_start(progress, False)
  283.         self.progresstext = label = gtk.Label('')
  284.         label.set_line_wrap(True)
  285.         label.set_use_markup(True)
  286.         label.set_alignment(0, 0)
  287.         label.show()
  288.         vbox.pack_start(label)
  289.         self.set_task(task)
  290.  
  291.     
  292.     def set_task(self, task):
  293.         self.progresstext.set_markup('<i>%s</i>' % task)
  294.  
  295.  
  296. UNKNOWN = 0
  297. SUCCESS = 1
  298. FAILURE = 2
  299. CANCELLED = 3
  300.  
  301. class RemuxProgressDialog(ProgressDialog):
  302.     
  303.     def __init__(self, parent, start, stop, fromname, toname):
  304.         ProgressDialog.__init__(self, 'Writing to disk', 'Writing the selected segment of <b>%s</b> to <b>%s</b>. This may take some time.' % (fromname, toname), 'Starting media pipeline', parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CANCEL, CANCELLED, gtk.STOCK_CLOSE, SUCCESS))
  305.         self.start = start
  306.         self.stop = stop
  307.         self.update_position(start)
  308.         self.set_completed(False)
  309.  
  310.     
  311.     def update_position(self, pos):
  312.         pos = min(max(pos, self.start), self.stop)
  313.         remaining = self.stop - pos
  314.         minutes = remaining // gst.SECOND * 60
  315.         seconds = (remaining - minutes * gst.SECOND * 60) // gst.SECOND
  316.         self.progress.set_text('%d:%02d of video remaining' % (minutes, seconds))
  317.         self.progress.set_fraction(1 - float(remaining) / (self.stop - self.start))
  318.  
  319.     
  320.     def set_completed(self, completed):
  321.         self.set_response_sensitive(CANCELLED, not completed)
  322.         self.set_response_sensitive(SUCCESS, completed)
  323.  
  324.  
  325.  
  326. def set_connection_blocked_async_marshalled(pads, proc, *args, **kwargs):
  327.     
  328.     def clear_list(l):
  329.         while l:
  330.             l.pop()
  331.  
  332.     to_block = list(pads)
  333.     to_relink = [ (x, x.get_peer()) for x in pads ]
  334.     
  335.     def on_pad_blocked_sync(pad, is_blocked):
  336.         if pad not in to_block:
  337.             return None
  338.         to_block.remove(pad)
  339.         if not to_block:
  340.             gobject.idle_add(on_pads_blocked)
  341.         
  342.  
  343.     
  344.     def on_pads_blocked():
  345.         for src, sink in to_relink:
  346.             src.link(sink)
  347.         
  348.         proc(*args, **kwargs)
  349.         for src, sink in to_relink:
  350.             src.set_blocked_async(False, (lambda : pass))
  351.         
  352.         clear_list(to_relink)
  353.  
  354.     for src, sink in to_relink:
  355.         src.unlink(sink)
  356.         src.set_blocked_async(True, on_pad_blocked_sync)
  357.     
  358.  
  359.  
  360. class Remuxer(gst.Pipeline):
  361.     __gsignals__ = {
  362.         'done': (gobject.SIGNAL_RUN_LAST, None, (int,)) }
  363.     
  364.     def __init__(self, fromuri, touri, start, stop):
  365.         self.__gobject_init__()
  366.         if not start >= 0:
  367.             raise AssertionError
  368.         if not stop > start:
  369.             raise AssertionError
  370.         self.fromuri = fromuri
  371.         self.touri = None
  372.         self.start_time = start
  373.         self.stop_time = stop
  374.         self.src = None
  375.         self.remuxbin = None
  376.         self.sink = None
  377.         self.resolution = UNKNOWN
  378.         self.window = None
  379.         self.pdialog = None
  380.         self._query_id = -1
  381.  
  382.     
  383.     def do_setup_pipeline(self):
  384.         self.src = gst.element_make_from_uri(gst.URI_SRC, self.fromuri)
  385.         self.remuxbin = RemuxBin(self.start_time, self.stop_time)
  386.         self.sink = gst.element_make_from_uri(gst.URI_SINK, self.touri)
  387.         self.resolution = UNKNOWN
  388.         if gobject.signal_lookup('allow-overwrite', self.sink.__class__):
  389.             self.sink.connect('allow-overwrite', (lambda : True))
  390.         
  391.         self.add(self.src, self.remuxbin, self.sink)
  392.         self.src.link(self.remuxbin)
  393.         self.remuxbin.link(self.sink)
  394.  
  395.     
  396.     def do_get_touri(self):
  397.         chooser = gtk.FileChooserDialog('Save as...', self.window, action = gtk.FILE_CHOOSER_ACTION_SAVE, buttons = (gtk.STOCK_CANCEL, CANCELLED, gtk.STOCK_SAVE, SUCCESS))
  398.         chooser.set_uri(self.fromuri)
  399.         chooser.unselect_all()
  400.         chooser.set_do_overwrite_confirmation(True)
  401.         name = self.fromuri.split('/')[-1][:-4] + '-remuxed.ogg'
  402.         chooser.set_current_name(name)
  403.         resp = chooser.run()
  404.         uri = chooser.get_uri()
  405.         chooser.destroy()
  406.         if resp == SUCCESS:
  407.             return uri
  408.         return None
  409.  
  410.     
  411.     def _start_queries(self):
  412.         
  413.         def do_query():
  414.             
  415.             try:
  416.                 pad = self.remuxbin.get_pad('src')
  417.                 (pos, duration) = pad.query_position(gst.FORMAT_TIME)
  418.                 if pos != gst.CLOCK_TIME_NONE:
  419.                     self.pdialog.update_position(pos)
  420.             except:
  421.                 pass
  422.  
  423.             return True
  424.  
  425.         if self._query_id == -1:
  426.             self._query_id = gobject.timeout_add(100, do_query)
  427.         
  428.  
  429.     
  430.     def _stop_queries(self):
  431.         if self._query_id != -1:
  432.             gobject.source_remove(self._query_id)
  433.             self._query_id = -1
  434.         
  435.  
  436.     
  437.     def _bus_watch(self, bus, message):
  438.         if message.type == gst.MESSAGE_ERROR:
  439.             print 'error', message
  440.             self._stop_queries()
  441.             m = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, 'Error processing file')
  442.             (gerror, debug) = message.parse_error()
  443.             txt = 'There was an error processing your file: %s\n\nDebug information:\n%s' % (gerror, debug)
  444.             m.format_secondary_text(txt)
  445.             m.run()
  446.             m.destroy()
  447.             self.response(FAILURE)
  448.         elif message.type == gst.MESSAGE_WARNING:
  449.             print 'warning', message
  450.         elif message.type == gst.MESSAGE_EOS:
  451.             name = self.touri
  452.             if name.startswith('file://'):
  453.                 name = name[7:]
  454.             
  455.             self.pdialog.set_task('Finished writing %s' % name)
  456.             self.pdialog.update_position(self.stop_time)
  457.             self._stop_queries()
  458.             self.pdialog.set_completed(True)
  459.         elif message.type == gst.MESSAGE_STATE_CHANGED:
  460.             if message.src == self:
  461.                 (old, new, pending) = message.parse_state_changed()
  462.                 if (old, new, pending) == (gst.STATE_READY, gst.STATE_PAUSED, gst.STATE_VOID_PENDING):
  463.                     self.pdialog.set_task('Processing file')
  464.                     self.pdialog.update_position(self.start_time)
  465.                     self._start_queries()
  466.                     self.set_state(gst.STATE_PLAYING)
  467.                 
  468.             
  469.         
  470.  
  471.     
  472.     def response(self, response):
  473.         if not self.resolution == UNKNOWN:
  474.             raise AssertionError
  475.         self.resolution = response
  476.         self.set_state(gst.STATE_NULL)
  477.         self.pdialog.destroy()
  478.         self.pdialog = None
  479.         self.window.set_sensitive(True)
  480.         self.emit('done', response)
  481.  
  482.     
  483.     def start(self, main_window):
  484.         self.window = main_window
  485.         self.touri = self.do_get_touri()
  486.         if not self.touri:
  487.             return False
  488.         self.do_setup_pipeline()
  489.         bus = self.get_bus()
  490.         bus.add_signal_watch()
  491.         bus.connect('message', self._bus_watch)
  492.         if self.window:
  493.             self.window.set_sensitive(False)
  494.         
  495.         fromname = self.fromuri.split('/')[-1]
  496.         toname = self.touri.split('/')[-1]
  497.         self.pdialog = RemuxProgressDialog(main_window, self.start_time, self.stop_time, fromname, toname)
  498.         self.pdialog.show()
  499.         self.pdialog.connect(('response',), (lambda w, r: self.response(r)))
  500.         self.set_state(gst.STATE_PAUSED)
  501.         return True
  502.  
  503.     
  504.     def run(self, main_window):
  505.         if self.start(main_window):
  506.             loop = gobject.MainLoop()
  507.             self.connect(('done',), (lambda : gobject.idle_add(loop.quit)))
  508.             loop.run()
  509.         else:
  510.             self.resolution = CANCELLED
  511.         return self.resolution
  512.  
  513.  
  514.  
  515. class RemuxBin(gst.Bin):
  516.     
  517.     def __init__(self, start_time, stop_time):
  518.         self.__gobject_init__()
  519.         self.parsefactories = self._find_parsers()
  520.         self.parsers = []
  521.         self.demux = gst.element_factory_make('oggdemux')
  522.         self.mux = gst.element_factory_make('oggmux')
  523.         self.add(self.demux, self.mux)
  524.         self.add_pad(gst.GhostPad('sink', self.demux.get_pad('sink')))
  525.         self.add_pad(gst.GhostPad('src', self.mux.get_pad('src')))
  526.         self.demux.connect('pad-added', self._new_demuxed_pad)
  527.         self.demux.connect('no-more-pads', self._no_more_pads)
  528.         self.start_time = start_time
  529.         self.stop_time = stop_time
  530.  
  531.     
  532.     def _find_parsers(self):
  533.         registry = gst.registry_get_default()
  534.         ret = { }
  535.         for f in registry.get_feature_list(gst.ElementFactory):
  536.             if f.get_klass().find('Parser') >= 0:
  537.                 for t in f.get_static_pad_templates():
  538.                     if t.direction == gst.PAD_SINK:
  539.                         for s in t.get_caps():
  540.                             ret[s.get_name()] = f.get_name()
  541.                         
  542.                         break
  543.                         continue
  544.                 
  545.         
  546.         return ret
  547.  
  548.     
  549.     def _new_demuxed_pad(self, element, pad):
  550.         format = pad.get_caps()[0].get_name()
  551.         if format not in self.parsefactories:
  552.             self.async_error('Unsupported media type: %s', format)
  553.             return None
  554.         queue = gst.element_factory_make('queue', 'queue_' + format)
  555.         queue.set_property('max-size-buffers', 1000)
  556.         parser = gst.element_factory_make(self.parsefactories[format])
  557.         self.add(queue)
  558.         self.add(parser)
  559.         queue.set_state(gst.STATE_PAUSED)
  560.         parser.set_state(gst.STATE_PAUSED)
  561.         pad.link(queue.get_compatible_pad(pad))
  562.         queue.link(parser)
  563.         parser.link(self.mux)
  564.         self.parsers.append(parser)
  565.  
  566.     
  567.     def _do_seek(self):
  568.         flags = gst.SEEK_FLAG_FLUSH
  569.         return self.demux.seek(1, gst.FORMAT_TIME, flags, gst.SEEK_TYPE_SET, self.start_time, gst.SEEK_TYPE_SET, self.stop_time)
  570.  
  571.     
  572.     def _no_more_pads(self, element):
  573.         pads = [ x.get_pad('src') for x in self.parsers ]
  574.         set_connection_blocked_async_marshalled(pads, self._do_seek)
  575.  
  576.  
  577.  
  578. class PlayerWindow(gtk.Window):
  579.     UPDATE_INTERVAL = 500
  580.     
  581.     def __init__(self):
  582.         gtk.Window.__init__(self)
  583.         self.set_default_size(600, 425)
  584.         self.create_ui()
  585.         self.player = GstPlayer(self.videowidget)
  586.         
  587.         def on_eos():
  588.             self.player.seek(0x0L)
  589.             self.play_toggled()
  590.  
  591.         
  592.         self.player.on_eos = lambda *x: on_eos()
  593.         self.update_id = -1
  594.         self.changed_id = -1
  595.         self.seek_timeout_id = -1
  596.         self.p_position = gst.CLOCK_TIME_NONE
  597.         self.p_duration = gst.CLOCK_TIME_NONE
  598.         
  599.         def on_delete_event():
  600.             self.player.stop()
  601.             gtk.main_quit()
  602.  
  603.         self.connect(('delete-event',), (lambda : on_delete_event()))
  604.  
  605.     
  606.     def load_file(self, location):
  607.         filename = location.split('/')[-1]
  608.         self.set_title('%s munger' % filename)
  609.         self.player.set_location(location)
  610.         if self.videowidget.flags() & gtk.REALIZED:
  611.             self.play_toggled()
  612.         else:
  613.             self.videowidget.connect_after(('realize',), (lambda : self.play_toggled()))
  614.  
  615.     
  616.     def create_ui(self):
  617.         vbox = gtk.VBox()
  618.         vbox.show()
  619.         self.add(vbox)
  620.         self.videowidget = VideoWidget()
  621.         self.videowidget.show()
  622.         vbox.pack_start(self.videowidget)
  623.         hbox = gtk.HBox()
  624.         hbox.show()
  625.         vbox.pack_start(hbox, fill = False, expand = False)
  626.         self.adjustment = gtk.Adjustment(0, 0, 100, 0.1, 1, 1)
  627.         hscale = gtk.HScale(self.adjustment)
  628.         hscale.set_digits(2)
  629.         hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)
  630.         hscale.connect('button-press-event', self.scale_button_press_cb)
  631.         hscale.connect('button-release-event', self.scale_button_release_cb)
  632.         hscale.connect('format-value', self.scale_format_value_cb)
  633.         hbox.pack_start(hscale)
  634.         hscale.show()
  635.         self.hscale = hscale
  636.         table = gtk.Table(2, 3)
  637.         table.show()
  638.         vbox.pack_start(table, fill = False, expand = False, padding = 6)
  639.         self.button = button = gtk.Button(stock = gtk.STOCK_MEDIA_PLAY)
  640.         button.set_property('can-default', True)
  641.         button.set_focus_on_click(False)
  642.         button.show()
  643.         bvbox = gtk.VBox()
  644.         bvbox.add(button)
  645.         bvbox.add(gtk.Button(stock = gtk.STOCK_MEDIA_PLAY))
  646.         bvbox.add(gtk.Button(stock = gtk.STOCK_MEDIA_PAUSE))
  647.         sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
  648.         for kid in bvbox.get_children():
  649.             sizegroup.add_widget(kid)
  650.         
  651.         bvbox.show()
  652.         table.attach(bvbox, 0, 1, 0, 2, gtk.FILL, gtk.FILL)
  653.         button.set_property('has-default', True)
  654.         button.connect(('clicked',), (lambda : self.play_toggled()))
  655.         self.cutin = cut = TimeControl(self, 'Cut in time')
  656.         cut.show()
  657.         table.attach(cut, 1, 2, 0, 1, gtk.EXPAND, 0, 12)
  658.         self.cutout = cut = TimeControl(self, 'Cut out time')
  659.         cut.show()
  660.         table.attach(cut, 1, 2, 1, 2, gtk.EXPAND, 0, 12)
  661.         button = gtk.Button('_Open other movie...')
  662.         button.show()
  663.         button.connect(('clicked',), (lambda : self.do_choose_file()))
  664.         table.attach(button, 2, 3, 0, 1, gtk.FILL, gtk.FILL)
  665.         button = gtk.Button('_Write to disk')
  666.         button.set_property('image', gtk.image_new_from_stock(gtk.STOCK_SAVE_AS, gtk.ICON_SIZE_BUTTON))
  667.         button.connect(('clicked',), (lambda : self.do_remux()))
  668.         button.show()
  669.         table.attach(button, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
  670.  
  671.     
  672.     def do_remux(self):
  673.         if self.player.is_playing():
  674.             self.play_toggled()
  675.         
  676.         in_uri = self.player.get_location()
  677.         out_uri = in_uri[:-4] + '-remuxed.ogg'
  678.         r = Remuxer(in_uri, out_uri, self.cutin.get_time(), self.cutout.get_time())
  679.         r.run(self)
  680.  
  681.     
  682.     def do_choose_file(self):
  683.         if self.player.is_playing():
  684.             self.play_toggled()
  685.         
  686.         chooser = gtk.FileChooserDialog('Choose a movie to cut cut cut', self, buttons = (gtk.STOCK_CANCEL, CANCELLED, gtk.STOCK_OPEN, SUCCESS))
  687.         chooser.set_local_only(False)
  688.         chooser.set_select_multiple(False)
  689.         f = gtk.FileFilter()
  690.         f.set_name('All files')
  691.         f.add_pattern('*')
  692.         chooser.add_filter(f)
  693.         f = gtk.FileFilter()
  694.         f.set_name('Ogg files')
  695.         f.add_pattern('*.ogg')
  696.         chooser.add_filter(f)
  697.         chooser.set_filter(f)
  698.         prev = self.player.get_location()
  699.         if prev:
  700.             chooser.set_uri(prev)
  701.         
  702.         resp = chooser.run()
  703.         uri = chooser.get_uri()
  704.         chooser.destroy()
  705.         if resp == SUCCESS:
  706.             self.load_file(uri)
  707.             return True
  708.         return False
  709.  
  710.     
  711.     def check_cutout(self):
  712.         if self.cutout.get_time() <= self.cutin.get_time():
  713.             (pos, dur) = self.player.query_position()
  714.             self.cutout.set_time(dur)
  715.         
  716.  
  717.     
  718.     def check_cutin(self):
  719.         if self.cutin.get_time() >= self.cutout.get_time():
  720.             self.cutin.set_time(0)
  721.         
  722.  
  723.     
  724.     def play_toggled(self):
  725.         if self.player.is_playing():
  726.             self.player.pause()
  727.             self.button.set_label(gtk.STOCK_MEDIA_PLAY)
  728.         else:
  729.             self.player.play()
  730.             if self.update_id == -1:
  731.                 self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL, self.update_scale_cb)
  732.             
  733.             self.button.set_label(gtk.STOCK_MEDIA_PAUSE)
  734.  
  735.     
  736.     def scale_format_value_cb(self, scale, value):
  737.         if self.p_duration == -1:
  738.             real = 0
  739.         else:
  740.             real = value * self.p_duration / 100
  741.         seconds = real / gst.SECOND
  742.         return '%02d:%02d' % (seconds / 60, seconds % 60)
  743.  
  744.     
  745.     def scale_button_press_cb(self, widget, event):
  746.         gst.debug('starting seek')
  747.         self.button.set_sensitive(False)
  748.         self.was_playing = self.player.is_playing()
  749.         if self.was_playing:
  750.             self.player.pause()
  751.         
  752.         if self.update_id != -1:
  753.             gobject.source_remove(self.update_id)
  754.             self.update_id = -1
  755.         
  756.         if self.changed_id == -1:
  757.             self.changed_id = self.hscale.connect('value-changed', self.scale_value_changed_cb)
  758.         
  759.  
  760.     
  761.     def scale_value_changed_cb(self, scale):
  762.         real = long(scale.get_value() * self.p_duration / 100)
  763.         gst.debug('value changed, perform seek to %r' % real)
  764.         self.player.seek(real)
  765.         self.player.get_state(timeout = 50 * gst.MSECOND)
  766.  
  767.     
  768.     def scale_button_release_cb(self, widget, event):
  769.         widget.disconnect(self.changed_id)
  770.         self.changed_id = -1
  771.         self.button.set_sensitive(True)
  772.         if self.seek_timeout_id != -1:
  773.             gobject.source_remove(self.seek_timeout_id)
  774.             self.seek_timeout_id = -1
  775.         else:
  776.             gst.debug('released slider, setting back to playing')
  777.             if self.was_playing:
  778.                 self.player.play()
  779.             
  780.         if self.update_id != -1:
  781.             self.error('Had a previous update timeout id')
  782.         else:
  783.             self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL, self.update_scale_cb)
  784.  
  785.     
  786.     def update_scale_cb(self):
  787.         had_duration = self.p_duration != gst.CLOCK_TIME_NONE
  788.         (self.p_position, self.p_duration) = self.player.query_position()
  789.         if self.p_position != gst.CLOCK_TIME_NONE:
  790.             value = self.p_position * 100 / self.p_duration
  791.             self.adjustment.set_value(value)
  792.             if not had_duration:
  793.                 self.cutin.set_time(0)
  794.             
  795.         
  796.         return True
  797.  
  798.  
  799.  
  800. def main(args):
  801.     
  802.     def usage():
  803.         sys.stderr.write('usage: %s [URI-OF-MEDIA-FILE]\n' % args[0])
  804.         return 1
  805.  
  806.     w = PlayerWindow()
  807.     w.show()
  808.     if len(args) == 1:
  809.         if not w.do_choose_file():
  810.             return 1
  811.     elif len(args) == 2:
  812.         if not gst.uri_is_valid(args[1]):
  813.             sys.stderr.write('Error: Invalid URI: %s\n' % args[1])
  814.             return 1
  815.         w.load_file(args[1])
  816.     else:
  817.         return usage()
  818.     gst.uri_is_valid(args[1]).main()
  819.  
  820. if __name__ == '__main__':
  821.     sys.exit(main(sys.argv))
  822.  
  823.